import os
import shutil
import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter import ttk
import requests
from PIL import Image, ImageOps
import threading
from datetime import datetime
import time
import random
import queue
import subprocess
import re
import json

# Пути к директориям и файлам
avatars_path = r"C:\Users\1\Desktop\VS 2022\Загрузка аватарок ВК\аватарки"
uploaded_path = os.path.join(avatars_path, "Загруженные")
tokens_file_path = r"C:\Users\1\Desktop\VS 2022\Загрузка аватарок ВК\токены для загруки аватарок.txt"
failed_accounts_file = r"C:\Users\1\Desktop\VS 2022\Загрузка аватарок ВК\неудачные.txt"
delay_settings_file = r"C:\Users\1\Desktop\VS 2022\Загрузка аватарок ВК\time.json"

# Глобальные переменные для токенов и списка аватарок
tokens = []
avatars = []

# Параметры рамки для обработки аватарки
border_size = 110
border_color = 'white'

# ----------------------------------------------------------------------------
# Очереди для логирования и статусы
# ----------------------------------------------------------------------------

log_queue = queue.Queue()
success_queue = queue.Queue()
fail_queue = queue.Queue()
ip_country_queue = queue.Queue()

# Объект Events для паузы и возобновления
pause_event = threading.Event()
pause_event.set()

# ----------------------------------------------------------------------------
# Переменные для таймеров
# ----------------------------------------------------------------------------
start_time = None
total_runtime = 0
next_action_time = 0
timer_running = False
countdown_running = False

# ----------------------------------------------------------------------------
# Создание главного окна с красивым оформлением
# ----------------------------------------------------------------------------

root = tk.Tk()
root.title("Загрузка аватарок ВК")
root.geometry("1200x700")  # Увеличена высота для таймеров
root.configure(bg="#2C3E50")

style = ttk.Style(root)
style.theme_use("clam")

default_font = ("Segoe UI", 11)
style.configure("TFrame", background="#2C3E50")
style.configure("TLabel", background="#2C3E50", foreground="#ECF0F1", font=default_font)
style.configure("TButton", font=("Segoe UI", 11, "bold"), padding=6)
style.configure("TEntry", padding=4, font=default_font)

style.map("TButton",
          foreground=[("active", "#ECF0F1")],
          background=[("active", "#2980B9")])

# ----------------------------------------------------------------------------
# Переменные для статистики и таймеров
# ----------------------------------------------------------------------------

total_accounts = tk.StringVar(value="Всего аккаунтов: 0")
processed_photos = tk.StringVar(value="Обработано фотографий: 0")
remaining_photos = tk.StringVar(value="Осталось фотографий: 0")
successful_uploads = tk.StringVar(value="Успешных добавлений: 0")
failed_uploads = tk.StringVar(value="Неуспешных добавлений: 0")
current_wifi_status = tk.StringVar(value="Wi-Fi: Сканирование...")
current_ip_country_status = tk.StringVar(value="IP: Определение... (Страна: ...)")

# Новые переменные для таймеров
runtime_status = tk.StringVar(value="Время работы: 00:00:00")
countdown_status = tk.StringVar(value="До следующего действия: --")

delay_min = tk.StringVar(value="20")
delay_max = tk.StringVar(value="40")

INFO_COLOR = "white"
SUCCESS_COLOR = "#2ECC71"
ERROR_COLOR = "#E74C3C"
SEPARATOR_COLOR = "#3498DB"
DEFAULT_TEXT_COLOR = "#ECF0F1"
TIMER_COLOR = "#F39C12"  # Цвет для таймеров

token_counter = 0
auto_scroll_enabled = tk.BooleanVar(value=True)

# Глобальные ссылки на метки для обновления цвета
ip_country_label_ui_ref = None
wifi_label_ui_ref = None
runtime_label_ui_ref = None
countdown_label_ui_ref = None

# ----------------------------------------------------------------------------
# Функции для сохранения и загрузки настроек задержки
# ----------------------------------------------------------------------------

def save_delay_settings():
    """Сохраняет текущие настройки задержки в JSON файл"""
    try:
        delay_data = {
            "delay_min": delay_min.get(),
            "delay_max": delay_max.get(),
            "last_updated": datetime.now().isoformat()
        }
        
        with open(delay_settings_file, 'w', encoding='utf-8') as f:
            json.dump(delay_data, f, indent=2, ensure_ascii=False)
        
        # Логируем только при первом сохранении или значительных изменениях
        # чтобы не засорять лог
        
    except Exception as e:
        log(f"Ошибка сохранения настроек задержки: {str(e)}", color=ERROR_COLOR)

def load_delay_settings():
    """Загружает настройки задержки из JSON файла"""
    try:
        if os.path.exists(delay_settings_file):
            with open(delay_settings_file, 'r', encoding='utf-8') as f:
                delay_data = json.load(f)
            
            # Устанавливаем значения в переменные
            delay_min.set(str(delay_data.get("delay_min", "20")))
            delay_max.set(str(delay_data.get("delay_max", "40")))
            
            log(f"Настройки задержки загружены: {delay_min.get()}-{delay_max.get()} сек", color=SUCCESS_COLOR)
            
        else:
            # Если файла нет, создаём его с дефолтными значениями
            save_delay_settings()
            log("Создан новый файл настроек задержки с значениями по умолчанию", color=INFO_COLOR)
            
    except Exception as e:
        log(f"Ошибка загрузки настроек задержки: {str(e)}", color=ERROR_COLOR)
        # Устанавливаем значения по умолчанию
        delay_min.set("20")
        delay_max.set("40")

def on_delay_change(*args):
    """Вызывается при изменении значений задержки"""
    # Сохраняем настройки в реальном времени
    save_delay_settings()

def on_closing():
    """Обработчик события закрытия окна"""
    try:
        # Сохраняем настройки перед закрытием
        save_delay_settings()
        log("Настройки задержки сохранены перед закрытием программы", color=SUCCESS_COLOR)
    except Exception as e:
        print(f"Ошибка при закрытии: {e}")
    finally:
        root.destroy()

# ----------------------------------------------------------------------------
# Функции для таймеров
# ----------------------------------------------------------------------------

def start_runtime_timer():
    """Запускает таймер общего времени работы"""
    global start_time, timer_running
    start_time = time.time()
    timer_running = True
    update_runtime_display()

def stop_runtime_timer():
    """Останавливает таймер общего времени работы"""
    global timer_running, total_runtime
    if timer_running and start_time:
        total_runtime += time.time() - start_time
        timer_running = False

def update_runtime_display():
    """Обновляет отображение времени работы"""
    global runtime_label_ui_ref
    if timer_running and start_time:
        current_runtime = total_runtime + (time.time() - start_time)
        hours = int(current_runtime // 3600)
        minutes = int((current_runtime % 3600) // 60)
        seconds = int(current_runtime % 60)
        runtime_status.set(f"Время работы: {hours:02d}:{minutes:02d}:{seconds:02d}")
        
        if runtime_label_ui_ref:
            runtime_label_ui_ref.configure(foreground=TIMER_COLOR)
    
    if timer_running:
        root.after(1000, update_runtime_display)

def start_countdown_timer(delay_seconds):
    """Запускает обратный отсчёт до следующего действия"""
    global next_action_time, countdown_running
    next_action_time = delay_seconds
    countdown_running = True
    update_countdown_display()

def update_countdown_display():
    """Обновляет отображение обратного отсчёта"""
    global next_action_time, countdown_running, countdown_label_ui_ref
    
    if countdown_running and next_action_time > 0:
        minutes = int(next_action_time // 60)
        seconds = int(next_action_time % 60)
        countdown_status.set(f"До следующего действия: {minutes:02d}:{seconds:02d}")
        
        if countdown_label_ui_ref:
            countdown_label_ui_ref.configure(foreground=TIMER_COLOR)
        
        next_action_time -= 1
        root.after(1000, update_countdown_display)
    else:
        countdown_running = False
        countdown_status.set("До следующего действия: --")
        if countdown_label_ui_ref:
            countdown_label_ui_ref.configure(foreground=DEFAULT_TEXT_COLOR)

# ----------------------------------------------------------------------------
# Функции логирования и работы с очередями
# ----------------------------------------------------------------------------

def log(message, color=INFO_COLOR):
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    log_queue.put((f"[{timestamp}] {message}\n", color))

def log_success_token(token_info: str):
    success_queue.put(token_info)

def log_fail_token(token_info: str):
    fail_queue.put(token_info)

def process_log_queue():
    try:
        while True:
            msg, color = log_queue.get_nowait()
            log_text.insert(tk.END, msg, color)
            log_text.tag_config(color, foreground=color)
            if auto_scroll_enabled.get():
                log_text.see(tk.END)
    except queue.Empty:
        pass
    root.after(100, process_log_queue)

def process_success_queue():
    try:
        while True:
            token_info = success_queue.get_nowait()
            success_text.insert(tk.END, token_info + "\n")
            if auto_scroll_enabled.get():
                success_text.see(tk.END)
    except queue.Empty:
        pass
    root.after(100, process_success_queue)

def process_fail_queue():
    try:
        while True:
            token_info = fail_queue.get_nowait()
            fail_text.insert(tk.END, token_info + "\n")
            if auto_scroll_enabled.get():
                fail_text.see(tk.END)
    except queue.Empty:
        pass
    root.after(100, process_fail_queue)

# ----------------------------------------------------------------------------
# Функция получения SSID Wi-Fi
# ----------------------------------------------------------------------------

def get_current_wifi_ssid():
    try:
        process = subprocess.Popen(['netsh', 'wlan', 'show', 'interfaces'],
                                     stdout=subprocess.PIPE,
                                     text=True,
                                     encoding='utf-8',
                                     errors='ignore',
                                     creationflags=subprocess.CREATE_NO_WINDOW)
        stdout, stderr = process.communicate(timeout=5)
        if process.returncode == 0:
            for line in stdout.splitlines():
                if "SSID" in line and "BSSID" not in line:
                    match = re.search(r":\s*(.+)", line)
                    if match:
                        ssid = match.group(1).strip()
                        if ssid:
                            return ssid
            return "Нет подключения"
        else:
            return "Ошибка опроса"
    except FileNotFoundError:
        return "netsh не найден"
    except subprocess.TimeoutExpired:
        return "Тайм-аут опроса"
    except Exception as e:
        return "Ошибка Wi-Fi"

def update_wifi_display():
    global wifi_label_ui_ref
    ssid = get_current_wifi_ssid()
    current_wifi_status.set(f"Wi-Fi: {ssid}")
    if "Ошибка" in ssid or "Нет подключения" in ssid or "не найден" in ssid:
        if wifi_label_ui_ref: 
            wifi_label_ui_ref.configure(foreground=ERROR_COLOR)
    else:
        if wifi_label_ui_ref: 
            wifi_label_ui_ref.configure(foreground=DEFAULT_TEXT_COLOR)
    root.after(5000, update_wifi_display)

# ----------------------------------------------------------------------------
# Функции для IP и страны
# ----------------------------------------------------------------------------

def get_external_ip_and_country():
    ip_address_val = "Ошибка (IP)"
    country_val = "Ошибка (Страна)"
    try:
        response = requests.get("http://ip-api.com/json/?fields=status,message,country,countryCode,query,city", timeout=4)
        response.raise_for_status()
        data = response.json()
        if data.get("status") == "success":
            ip_address_val = data.get("query", ip_address_val)
            country_name = data.get("country")
            country_code = data.get("countryCode")
            city_name = data.get("city", "")

            if country_name:
                country_val = country_name
            elif country_code:
                country_val = country_code
            else:
                country_val = "N/A"
            
            if city_name and country_val != "N/A":
                country_val = f"{city_name}, {country_val}"
            elif city_name:
                country_val = city_name

        else:
            error_message = data.get("message", "Неизвестная ошибка от ip-api.com")
            log(f"Ошибка от ip-api.com (status fail): {error_message}", color=ERROR_COLOR)
    except requests.exceptions.Timeout:
        log(f"Таймаут при запросе к ip-api.com", color=ERROR_COLOR)
    except requests.exceptions.RequestException as e:
        log(f"Ошибка запроса к ip-api.com: {e}", color=ERROR_COLOR)
    except Exception as e:
        log(f"Непредвиденная ошибка в get_external_ip_and_country: {e}", color=ERROR_COLOR)
    return ip_address_val, country_val

def _fetch_ip_and_country_data():
    ip, country = get_external_ip_and_country()
    ip_country_queue.put((ip, country))

def process_ip_country_queue():
    global ip_country_label_ui_ref
    try:
        ip, country = ip_country_queue.get_nowait()
        text_to_set = f"IP: {ip} (Локация: {country})"
        current_ip_country_status.set(text_to_set)
        
        if ip_country_label_ui_ref:
            if "Ошибка" in ip or "Ошибка" in country or "N/A" in country:
                ip_country_label_ui_ref.configure(foreground=ERROR_COLOR)
            else:
                ip_country_label_ui_ref.configure(foreground=DEFAULT_TEXT_COLOR)
    except queue.Empty:
        pass
    root.after(200, process_ip_country_queue)

def update_ip_country_display():
    threading.Thread(target=_fetch_ip_and_country_data, daemon=True).start()
    root.after(15000, update_ip_country_display)

# ----------------------------------------------------------------------------
# Функции работы с файлами и обновление информации
# ----------------------------------------------------------------------------

def log_failed_account(token):
    try:
        with open(failed_accounts_file, 'a', encoding='utf-8') as file:
            file.write(f"{token}\n")
        log(f"Записан неудачный токен: {token}", color=ERROR_COLOR)
    except Exception as e:
        log(f"Ошибка записи неудачного токена: {str(e)}", color=ERROR_COLOR)

def load_tokens():
    global tokens
    if os.path.exists(tokens_file_path):
        with open(tokens_file_path, 'r', encoding='utf-8') as file:
            tokens = [line.strip() for line in file if line.strip()]
        total_accounts.set(f"Всего аккаунтов: {len(tokens)}")
        log(f"Загружено токенов: {len(tokens)}", color=SUCCESS_COLOR)
    else:
        log("Файл с токенами не найден.", color=ERROR_COLOR)

def load_avatars():
    global avatars
    if os.path.exists(avatars_path):
        avatars = [os.path.join(avatars_path, f)
                   for f in os.listdir(avatars_path)
                   if os.path.isfile(os.path.join(avatars_path, f)) and f != "prepared_image.png"]
        remaining_photos.set(f"Осталось фотографий: {len(avatars)}")
        log(f"Загружено аватарок: {len(avatars)}", color=SUCCESS_COLOR)
    else:
        log("Директория с аватарками не найдена.", color=ERROR_COLOR)

def update_info():
    log("Нажата кнопка 'Обновить информацию'", color=INFO_COLOR)
    load_tokens()
    load_avatars()
    processed_photos.set("Обработано фотографий: 0")
    successful_uploads.set("Успешных добавлений: 0")
    failed_uploads.set("Неуспешных добавлений: 0")
    os.makedirs(uploaded_path, exist_ok=True)
    log("Информация обновлена.", color=SUCCESS_COLOR)

# ----------------------------------------------------------------------------
# Функция для перезапуска неуспешных токенов
# ----------------------------------------------------------------------------

def restart_failed_tokens():
    """Перезапускает неуспешные токены из поля 'Неуспешные токены'"""
    global tokens
    
    # Получаем все токены из поля неуспешных токенов
    failed_tokens_text = fail_text.get("1.0", tk.END).strip()
    
    if not failed_tokens_text:
        log("Нет неуспешных токенов для перезапуска.", color=INFO_COLOR)
        messagebox.showinfo("Информация", "Нет неуспешных токенов для перезапуска.")
        return
    
    # Разбиваем текст на строки и извлекаем токены
    failed_tokens_lines = failed_tokens_text.split('\n')
    failed_tokens_list = []
    
    for line in failed_tokens_lines:
        line = line.strip()
        if line:
            failed_tokens_list.append(line)
    
    if not failed_tokens_list:
        log("Не найдено валидных токенов для перезапуска.", color=ERROR_COLOR)
        messagebox.showwarning("Предупреждение", "Не найдено валидных токенов для перезапуска.")
        return
    
    # Подтверждение перезапуска
    result = messagebox.askyesno("Подтвердите перезапуск", 
                                f"Вы уверены, что хотите перезапустить {len(failed_tokens_list)} неуспешных токенов?\n\n"
                                "Они будут добавлены в начало очереди для повторной обработки.")
    
    if not result:
        return
    
    # Добавляем неуспешные токены в начало списка tokens
    tokens = failed_tokens_list + tokens  # Добавляем в начало для приоритетной обработки
    
    # Обновляем счетчик аккаунтов
    total_accounts.set(f"Всего аккаунтов: {len(tokens)}")
    
    # Очищаем поле неуспешных токенов
    fail_text.delete("1.0", tk.END)
    
    # Сбрасываем счетчик неуспешных добавлений
    current_failed = failed_uploads.get()
    current_count = int(current_failed.split(": ")[1])
    new_count = current_count - len(failed_tokens_list)
    if new_count < 0:
        new_count = 0
    failed_uploads.set(f"Неуспешных добавлений: {new_count}")
    
    # Очищаем файл неудачных аккаунтов
    try:
        if os.path.exists(failed_accounts_file):
            with open(failed_accounts_file, 'w', encoding='utf-8') as file:
                file.write("")  # Очищаем файл
        log("Файл неудачных аккаунтов очищен.", color=SUCCESS_COLOR)
    except Exception as e:
        log(f"Ошибка очистки файла неудачных аккаунтов: {str(e)}", color=ERROR_COLOR)
    
    log(f"Перезапущено {len(failed_tokens_list)} неуспешных токенов. Они добавлены в очередь для повторной обработки.", color=SUCCESS_COLOR)
    messagebox.showinfo("Успех", f"Успешно перезапущено {len(failed_tokens_list)} токенов!\n\nОни добавлены в начало очереди для приоритетной обработки.")

def get_user_info(token):
    url = "https://api.vk.com/method/users.get"
    params = {'access_token': token, 'v': '5.131'}
    try:
        response = requests.get(url, params=params, timeout=10)
        if response.status_code == 200:
            data = response.json()
            if 'response' in data and len(data['response']) > 0:
                return data['response'][0]
        log(f"Ошибка получения информации о пользователе: {response.text}", color=ERROR_COLOR)
    except Exception as e:
        log(f"Исключение при получении информации о пользователе: {str(e)}", color=ERROR_COLOR)
    return None

def get_upload_url(token):
    url = "https://api.vk.com/method/photos.getOwnerPhotoUploadServer"
    params = {'access_token': token, 'v': '5.131'}
    try:
        response = requests.get(url, params=params, timeout=10)
        if response.status_code == 200:
            return response.json().get('response', {}).get('upload_url')
        else:
            log(f"Ошибка получения URL: {response.text}", color=ERROR_COLOR)
    except Exception as e:
        log(f"Исключение при получении URL: {str(e)}", color=ERROR_COLOR)
    return None

def save_photo(data, token):
    url = "https://api.vk.com/method/photos.saveOwnerPhoto"
    params = {
        'server': data.get('server'),
        'photo': data.get('photo'),
        'hash': data.get('hash'),
        'access_token': token,
        'v': '5.131'
    }
    try:
        response = requests.post(url, params=params, timeout=10)
        if response.status_code == 200:
            return response.json().get('response', {})
    except Exception as e:
        log(f"Исключение при сохранении фото: {str(e)}", color=ERROR_COLOR)
    return {}

def delete_last_wall_post(token):
    url = "https://api.vk.com/method/wall.get"
    params = {'access_token': token, 'v': '5.131', 'count': 1}
    try:
        response = requests.get(url, params=params, timeout=10)
        data = response.json()
        if response.status_code == 200 and 'response' in data:
            items = data['response'].get('items', [])
            if items:
                last_post_id = items[0]['id']
                log(f"Последняя запись найдена: ID {last_post_id}", color=INFO_COLOR)
                delete_url = "https://api.vk.com/method/wall.delete"
                delete_params = {'access_token': token, 'v': '5.131', 'post_id': last_post_id}
                delete_response = requests.post(delete_url, params=delete_params, timeout=10)
                if delete_response.status_code == 200:
                    log(f"Последняя запись успешно удалена: ID {last_post_id}", color=SUCCESS_COLOR)
                else:
                    log(f"Ошибка при удалении записи: {delete_response.text}", color=ERROR_COLOR)
            else:
                log("Нет записей для удаления.", color=INFO_COLOR)
        else:
            log(f"Ошибка получения записей со стены: {response.text}", color=ERROR_COLOR)
    except Exception as e:
        log(f"Исключение при удалении записи: {str(e)}", color=ERROR_COLOR)

def prepare_image(image_path):
    try:
        image = Image.open(image_path)
        if image.width != image.height:
            new_size = max(image.width, image.height)
            new_image = Image.new("RGBA", (new_size, new_size), (255, 255, 255, 0))
            new_image.paste(image, ((new_size - image.width) // 2, (new_size - image.height) // 2))
        else:
            new_image = image.convert("RGBA")

        new_image_with_border = ImageOps.expand(new_image, border=border_size, fill=border_color)
        
        prepared_image_path = os.path.join(avatars_path, "prepared_image.png")
        new_image_with_border.save(prepared_image_path, format="PNG")
        return prepared_image_path
    except Exception as e:
        log(f"Ошибка подготовки изображения: {str(e)}", color=ERROR_COLOR)
        return None

def upload_avatar():
    global token_counter
    processed_count = 0
    success_count = 0
    fail_count = 0

    while tokens and avatars:
        pause_event.wait()

        token_counter += 1
        token = tokens.pop(0)
        avatar_file = avatars.pop(0)

        processed_count += 1
        processed_photos.set(f"Обработано фотографий: {processed_count}")
        remaining_photos.set(f"Осталось фотографий: {len(avatars)}")

        user_info = get_user_info(token)
        user_name = f"{user_info.get('first_name', '')} {user_info.get('last_name', '')}" if user_info else "Неизвестный пользователь"

        log(f"{token_counter}. Токен (пользователь: {user_name}): {token}", color=INFO_COLOR)
        log(f"Обработка файла: {avatar_file}", color=INFO_COLOR)

        upload_url = get_upload_url(token)
        if not upload_url:
            log(f"Не удалось получить URL для загрузки для {user_name}.", color=ERROR_COLOR)
            fail_count += 1
            failed_uploads.set(f"Неуспешных добавлений: {fail_count}")
            log_fail_token(token)
            log_failed_account(token)
            try: 
                os.remove(avatar_file)
            except Exception as e: 
                log(f"Ошибка удаления файла: {str(e)}", color=ERROR_COLOR)
            delay_and_continue()
            continue

        upload_success = False
        response_from_server = None
        for attempt in range(1, 4):
            prepared_image_path = prepare_image(avatar_file)
            if not prepared_image_path:
                log(f"Попытка {attempt}: Не удалось подготовить изображение для {user_name}.", color=ERROR_COLOR)
                continue
            try:
                with open(prepared_image_path, 'rb') as file_to_upload:
                    files = {'photo': file_to_upload}
                    response_from_server = requests.post(upload_url, files=files, timeout=20)
                if response_from_server.status_code != 200:
                    log(f"Попытка {attempt}: Ошибка загрузки фото для {user_name}: {response_from_server.text}", color=ERROR_COLOR)
                else:
                    upload_success = True
                    break
            except Exception as e:
                log(f"Попытка {attempt}: Исключение при загрузке: {str(e)}", color=ERROR_COLOR)
            finally:
                if os.path.exists(prepared_image_path):
                    try: 
                        os.remove(prepared_image_path)
                    except Exception as e: 
                        log(f"Ошибка удаления подготовленного изображения: {str(e)}", color=ERROR_COLOR)
            if attempt < 3 and not upload_success:
                log(f"Повторная попытка загрузки фото для {user_name}...", color=INFO_COLOR)
                time.sleep(1)

        if not upload_success:
            log(f"Не удалось загрузить фото для {user_name} после 3 попыток.", color=ERROR_COLOR)
            fail_count += 1
            failed_uploads.set(f"Неуспешных добавлений: {fail_count}")
            log_fail_token(token)
            log_failed_account(token)
            try: 
                os.remove(avatar_file)
            except Exception as e: 
                log(f"Ошибка удаления неудачной фотографии: {str(e)}", color=ERROR_COLOR)
            delay_and_continue()
            continue

        log(f"Фото успешно загружено на сервер для {user_name}.", color=SUCCESS_COLOR)
        try: 
            upload_data = response_from_server.json()
        except Exception as e: 
            log(f"Ошибка преобразования ответа в JSON: {str(e)}", color=ERROR_COLOR)
            upload_data = {}
        
        save_response = save_photo(upload_data, token)
        if save_response.get("saved") == 1:
            log(f"Фото сохранено для {user_name}. ID поста: {save_response.get('post_id')}", color=SUCCESS_COLOR)
            delete_last_wall_post(token)
            success_count += 1
            successful_uploads.set(f"Успешных добавлений: {success_count}")
            log_success_token(token)
        else:
            log(f"Ошибка сохранения фото для {user_name}. Ответ: {save_response}", color=ERROR_COLOR)
            fail_count += 1
            failed_uploads.set(f"Неуспешных добавлений: {fail_count}")
            log_fail_token(token)
            log_failed_account(token)
        
        try:
            destination_path = os.path.join(uploaded_path, os.path.basename(avatar_file))
            shutil.move(avatar_file, destination_path)
        except Exception as e: 
            log(f"Ошибка перемещения оригинального файла: {str(e)}", color=ERROR_COLOR)
        
        log("—" * 50, color=SEPARATOR_COLOR)
        delay_and_continue()

    log("Загрузка аватарок завершена. Все операции выполнены.", color=SUCCESS_COLOR)
    stop_runtime_timer()  # Останавливаем таймер общего времени
    if upload_button: 
        upload_button["state"] = "normal"
    if pause_button: 
        pause_button["state"] = "disabled"
    if resume_button: 
        resume_button["state"] = "disabled"

def delay_and_continue():
    try:
        min_delay_val = float(delay_min.get())
        max_delay_val = float(delay_max.get())
        if max_delay_val < min_delay_val: 
            max_delay_val = min_delay_val
        delay = random.uniform(min_delay_val, max_delay_val)
        log(f"Задержка перед следующим аккаунтом: {delay:.2f} секунд", color=INFO_COLOR)
        
        # Запускаем обратный отсчёт
        start_countdown_timer(int(delay))
        
        time.sleep(delay)
    except ValueError:
        log("Ошибка в значениях задержки. Установлена задержка: 2.5 секунды.", color=ERROR_COLOR)
        start_countdown_timer(3)  # Показываем обратный отсчёт для стандартной задержки
        time.sleep(2.5)

def start_upload_thread():
    log("Нажата кнопка 'Запустить'", color=INFO_COLOR)
    upload_button["state"] = "disabled"
    pause_button["state"] = "normal"
    resume_button["state"] = "disabled"
    pause_event.set()
    
    # Запускаем таймер общего времени работы
    start_runtime_timer()
    
    threading.Thread(target=upload_avatar, daemon=True).start()

def pause_process():
    log("Нажата кнопка 'Приостановить'", color=INFO_COLOR)
    pause_event.clear()
    pause_button["state"] = "disabled"
    resume_button["state"] = "normal"
    
    # Останавливаем таймер при паузе
    stop_runtime_timer()

def resume_process():
    log("Нажата кнопка 'Восстановить'", color=INFO_COLOR)
    pause_event.set()
    resume_button["state"] = "disabled"
    pause_button["state"] = "normal"
    
    # Возобновляем таймер
    start_runtime_timer()

def clear_all_logs():
    log_text.delete(1.0, tk.END)
    success_text.delete(1.0, tk.END)
    fail_text.delete(1.0, tk.END)

last_clicked_text_widget = None

def create_context_menu(event):
    global last_clicked_text_widget
    last_clicked_text_widget = event.widget
    context_menu.tk_popup(event.x_root, event.y_root)

def copy_selected_text():
    global last_clicked_text_widget
    if not last_clicked_text_widget: 
        return
    try:
        selected_text = last_clicked_text_widget.selection_get()
        root.clipboard_clear()
        root.clipboard_append(selected_text)
    except tk.TclError: 
        pass

# ----------------------------------------------------------------------------
# Построение интерфейса
# ----------------------------------------------------------------------------

main_frame = ttk.Frame(root, padding=10)
main_frame.pack(fill=tk.BOTH, expand=True)

# Верхняя панель кнопок - первая строка
top_frame = ttk.Frame(main_frame)
top_frame.pack(fill=tk.X, pady=5)

upload_button = ttk.Button(top_frame, text="Запустить", command=start_upload_thread, width=15)
upload_button.pack(side=tk.LEFT, padx=5)
pause_button = ttk.Button(top_frame, text="Приостановить", command=pause_process, width=15, state="disabled")
pause_button.pack(side=tk.LEFT, padx=5)
resume_button = ttk.Button(top_frame, text="Восстановить", command=resume_process, width=15, state="disabled")
resume_button.pack(side=tk.LEFT, padx=5)
update_button = ttk.Button(top_frame, text="Обновить информацию", command=update_info, width=20)
update_button.pack(side=tk.LEFT, padx=5)

delay_label = ttk.Label(top_frame, text="Задержка (сек):")
delay_label.pack(side=tk.LEFT, padx=(10, 5))
delay_min_entry = ttk.Entry(top_frame, textvariable=delay_min, width=5)
delay_min_entry.pack(side=tk.LEFT)
delay_dash_label = ttk.Label(top_frame, text="-")
delay_dash_label.pack(side=tk.LEFT)
delay_max_entry = ttk.Entry(top_frame, textvariable=delay_max, width=5)
delay_max_entry.pack(side=tk.LEFT)

# Вторая строка кнопок
top_frame2 = ttk.Frame(main_frame)
top_frame2.pack(fill=tk.X, pady=(0, 5))

clear_log_button = ttk.Button(top_frame2, text="Очистить все журналы", command=clear_all_logs, width=18)
clear_log_button.pack(side=tk.LEFT, padx=5)

# Кнопка для перезапуска неуспешных токенов
restart_failed_button = ttk.Button(top_frame2, text="Перезапустить неуспешные", command=restart_failed_tokens, width=22)
restart_failed_button.pack(side=tk.LEFT, padx=5)

stats_frame = ttk.Frame(main_frame)
stats_frame.pack(fill=tk.X, pady=5)

# Основная статистика
ttk.Label(stats_frame, textvariable=total_accounts).pack(anchor=tk.W)
ttk.Label(stats_frame, textvariable=processed_photos).pack(anchor=tk.W)
ttk.Label(stats_frame, textvariable=remaining_photos).pack(anchor=tk.W)
ttk.Label(stats_frame, textvariable=successful_uploads).pack(anchor=tk.W)
ttk.Label(stats_frame, textvariable=failed_uploads).pack(anchor=tk.W)

# Разделитель
separator = ttk.Separator(stats_frame, orient='horizontal')
separator.pack(fill=tk.X, pady=(5, 5))

# Информация о подключении
wifi_label_ui_ref = ttk.Label(stats_frame, textvariable=current_wifi_status)
wifi_label_ui_ref.pack(anchor=tk.W)

ip_country_label_ui_ref = ttk.Label(stats_frame, textvariable=current_ip_country_status)
ip_country_label_ui_ref.pack(anchor=tk.W, pady=(2, 0))

# Разделитель
separator2 = ttk.Separator(stats_frame, orient='horizontal')
separator2.pack(fill=tk.X, pady=(5, 5))

# Таймеры
runtime_label_ui_ref = ttk.Label(stats_frame, textvariable=runtime_status, font=("Segoe UI", 11, "bold"))
runtime_label_ui_ref.pack(anchor=tk.W)

countdown_label_ui_ref = ttk.Label(stats_frame, textvariable=countdown_status, font=("Segoe UI", 11, "bold"))
countdown_label_ui_ref.pack(anchor=tk.W, pady=(2, 0))

bottom_frame = ttk.Frame(main_frame)
bottom_frame.pack(fill=tk.BOTH, expand=True, pady=(5, 0))
bottom_frame.columnconfigure(0, weight=1)
bottom_frame.columnconfigure(1, weight=1) 
bottom_frame.columnconfigure(2, weight=1)

log_frame = ttk.Frame(bottom_frame, relief=tk.SUNKEN)
log_frame.grid(row=0, column=0, sticky="nsew", padx=5, pady=5)
ttk.Label(log_frame, text="Общий журнал").pack(anchor="n")
log_text = tk.Text(log_frame, height=15, wrap=tk.WORD, bg="#34495E", fg="white", font=default_font)
log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=2, pady=2)
log_scroll = ttk.Scrollbar(log_frame, orient=tk.VERTICAL, command=log_text.yview)
log_scroll.pack(side=tk.RIGHT, fill=tk.Y)
log_text.config(yscrollcommand=log_scroll.set)

success_frame = ttk.Frame(bottom_frame, relief=tk.SUNKEN)
success_frame.grid(row=0, column=1, sticky="nsew", padx=5, pady=5)
ttk.Label(success_frame, text="Успешные токены").pack(anchor="n")
success_text = tk.Text(success_frame, height=15, wrap=tk.WORD, bg="#34495E", fg="white", font=default_font)
success_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=2, pady=2)
success_scroll = ttk.Scrollbar(success_frame, orient=tk.VERTICAL, command=success_text.yview)
success_scroll.pack(side=tk.RIGHT, fill=tk.Y)
success_text.config(yscrollcommand=success_scroll.set)

fail_frame = ttk.Frame(bottom_frame, relief=tk.SUNKEN)
fail_frame.grid(row=0, column=2, sticky="nsew", padx=5, pady=5)
ttk.Label(fail_frame, text="Неуспешные токены").pack(anchor="n")
fail_text = tk.Text(fail_frame, height=15, wrap=tk.WORD, bg="#34495E", fg="white", font=default_font)
fail_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=2, pady=2)
fail_scroll = ttk.Scrollbar(fail_frame, orient=tk.VERTICAL, command=fail_text.yview)
fail_scroll.pack(side=tk.RIGHT, fill=tk.Y)
fail_text.config(yscrollcommand=fail_scroll.set)

context_menu = tk.Menu(root, tearoff=0)
context_menu.add_command(label="Копировать", command=copy_selected_text)
context_menu.add_separator()
context_menu.add_checkbutton(label="Автопрокрутка", variable=auto_scroll_enabled)

log_text.bind("<Button-3>", create_context_menu)
success_text.bind("<Button-3>", create_context_menu)
fail_text.bind("<Button-3>", create_context_menu)

# ----------------------------------------------------------------------------
# Привязка событий для автоматического сохранения настроек задержки
# ----------------------------------------------------------------------------

# Добавляем обработчики событий для полей задержки
delay_min.trace('w', on_delay_change)
delay_max.trace('w', on_delay_change)

# Добавляем обработчик закрытия окна
root.protocol("WM_DELETE_WINDOW", on_closing)

# ----------------------------------------------------------------------------
# Запуск обработки очередей и первоначальное обновление информации
# ----------------------------------------------------------------------------

process_log_queue()
process_success_queue()
process_fail_queue()

# Загружаем настройки задержки при запуске
load_delay_settings()

update_info()

# Запуск периодического обновления Wi-Fi и IP/Страны
update_wifi_display()
process_ip_country_queue()
update_ip_country_display()

root.mainloop()
